﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Research.Kinect.Nui;
using Point = System.Windows.Point;
using System.Collections;
using Vector = Microsoft.Research.Kinect.Nui.Vector;
using Camera = Microsoft.Research.Kinect.Nui.Camera;

using System.Windows.Media.Media3D;
using WaveSim;
using System.Windows.Threading;

namespace Rowing
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Vector3D zoomDelta;

        private bool _rendering;
        private double _lastTimeRendered;
        private Random _rnd = new Random(1234);

        // Raindrop parameters.  Negative amplitude causes little tower of 
        // water to jump up vertically in the instant after the drop hits.
        private double _splashAmplitude; // Average height (depth, since negative) of raindrop splashes.  
        private double _splashDelta = 0.5;      // Actual splash height is Ampl +/- Delta (random)
        private double _raindropPeriodInMS;
        private double _waveHeight = 1.0;
        private int _dropSize;

        // Values to try:
        //   GridSize=20, RenderPeriod=125
        //   GridSize=50, RenderPeriod=50
        public const int gridSize = 20; //50;    
        private const double RenderPeriodInMS = 60; //50;

        //private const double FLOOR = -1;
        //private const double ROOF = 2;
        //private Point3D boatPosition = new Point3D(-5, FLOOR, -5);

        private WaveGrid waveGrid;
        public Boat boat;
        private TimeChecker timeChecker;
        private KinectNui nui;
        private DispatcherTimer timer;
        private DispatcherTimer printTimer;
        private bool isFirstStart;
        private bool isFinished;
        public int initObjectNum
        {
            get;
            set;
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            isFirstStart = false;
            Initialize();
            isFirstStart = true;
            isFinished = false;
        }

        private void Initialize()
        {
            try
            {
                _rendering = false;
                
                boat = new Boat();
                timeChecker = new TimeChecker();
                timeChecker.Start();
                if (waveGrid == null)
                {
                    _splashAmplitude = -1.0;
                    _raindropPeriodInMS = 1000.0;
                    _dropSize = 2;
                    waveGrid = new WaveGrid(boat.groundSize);
                    meshMain.Positions = waveGrid.Points;
                    meshMain.TriangleIndices = waveGrid.TriangleIndices;
                    CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
                    _rendering = true;
                }
                //if (!isFirstStart)
                {
                    nui = new KinectNui(this);
                    nui.Initialize();
                    PrintMessage(Properties.Resources.KinectConnect);
                }
            }
            catch (InvalidOperationException)
            {
                // 화면에 에러 출력
                PrintMessage(Properties.Resources.NoKinectError);
            }
            catch (Exception ex)
            {
                PrintMessage(ex.Message);
            }
        }

        private void PrintMessage(string str)
        {
            warnWindow.Content = str;
            warnWindow.Visibility = Visibility.Visible;
            printTimer = new DispatcherTimer();
            //Set the timer interval to the length of the animation.
            printTimer.Interval = new TimeSpan(TimeSpan.TicksPerSecond * 2);
            printTimer.Tick += (EventHandler)delegate(object snd, EventArgs ea)
            {
                warnWindow.Content = "";
                ((DispatcherTimer)snd).Stop();
            };
            printTimer.Start();
        }

        private void StartTimer()
        {
            //timer = new DispatcherTimer();
            //timer.Interval = TimeSpan.FromMilliseconds(50);
            //timer.Tick += new EventHandler(processHandler);
            //timer.Start();
            PrintMessage("Let's get started!!");
        }

        //private void processHandler(Object sender, EventArgs args)
        public void processHandler()
        {
            // 보트의 이동속도 계산
            if (timeChecker.Processing())
            {
                nui.CalcSpeed();
            }
            //if (_rendering)
            //{
            //    CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
            //    _rendering = false;
            //}
            // 현재 위치 출력
            boat.Display(ground);
            Speed.Text = string.Format("( {0:0.0000}, {0:0.0000} )", boat.currVelocity.X, boat.currVelocity.Z);
            //Speed.Text = boat.currPos.X + " " + boat.currPos.Z;
            // ground 이탈
            if (!boat.CalcMoving())
            {
                PrintMessage(Properties.Resources.GameOver);
                //if you want this event handler executed for just once
                Destroy();
                isFinished = true;
            }
        }

        public void NuiVideoFrameReady(PlanarImage Image)
        {
            //image.Source = BitmapSource.Create(
            //    Image.Width, Image.Height, 96, 96, PixelFormats.Bgr32, null,
            //    Image.Bits, Image.Width * Image.BytesPerPixel);
            //Processing();
        }

        public void SkeletonFrameReady()
        {
            //imageContainer.Children.RemoveRange(initCanvasCount, imageContainer.Children.Count - initCanvasCount);
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            nui.Destroy();
        }

        private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            //if (e.Delta > 0)
                // Zoom in
                //camMain.Position = Point3D.Add(camMain.Position, zoomDelta);
            //else
                // Zoom out
                //camMain.Position = Point3D.Subtract(camMain.Position, zoomDelta);
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            RenderingEventArgs rargs = (RenderingEventArgs)e;
            if ((rargs.RenderingTime.TotalMilliseconds - _lastTimeRendered) > RenderPeriodInMS)
            {
                // Unhook Positions collection from our mesh, for performance
                // (see http://blogs.msdn.com/timothyc/archive/2006/08/31/734308.aspx)
                meshMain.Positions = null;

                // Do the next iteration on the water grid, propagating waves
                double NumDropsThisTime = RenderPeriodInMS / _raindropPeriodInMS;

                // Result at this point for number of drops is something like
                // 2.25.  We'll induce integer portion (e.g. 2 drops), then
                // 25% chance for 3rd drop.
                int NumDrops = (int)NumDropsThisTime;   // trunc
                for (int i = 0; i < NumDrops; i++)
                    waveGrid.SetRandomPeak(_splashAmplitude, _splashDelta, _dropSize);

                if ((NumDropsThisTime - NumDrops) > 0)
                {
                    double DropChance = NumDropsThisTime - NumDrops;
                    if (_rnd.NextDouble() <= DropChance)
                        waveGrid.SetRandomPeak(_splashAmplitude, _splashDelta, _dropSize);
                }

                waveGrid.ProcessWater();

                // Then update our mesh to use new Z values
                meshMain.Positions = waveGrid.Points;

                _lastTimeRendered = rargs.RenderingTime.TotalMilliseconds;
            }
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if(Keyboard.IsKeyDown(Key.Up))
            {
                boat.SetSpeed(new Vector3D(0, 0, -0.2));
            }
            if(Keyboard.IsKeyDown(Key.Down))
            {
                boat.SetSpeed(new Vector3D(0, 0, 0.2));
            }
            if(Keyboard.IsKeyDown(Key.Left))
            {
                boat.SetSpeed(new Vector3D(-0.2, 0, 0));
            }
            if (Keyboard.IsKeyDown(Key.Right))
            {
                boat.SetSpeed(new Vector3D(0.2, 0, 0));
            }
        }

        private void btnResume_Click(object sender, RoutedEventArgs e)
        {
            if(!isFinished)
                Destroy();
        }

        private void BtnStartClick(object sender, RoutedEventArgs e)
        {
            StartTimer();
            Initialize();
            isFirstStart = false;
        }

        private void Destroy()
        {
            if (timer != null && timer.IsEnabled)
                timer.Stop();
            ground.Children.Clear();
            nui.Destroy();
            boat.Destroy();
        }
    }
}
